#include "FPCH.h"

#include "FLibrary.h"

#include "FLog.h"

#include "FFamily.h"
#include "FCharMap.h"
#include "FKerningTable.h"
#include "FMetrics.h"
#include "FFactory.h"

#include "FFactoryOpenGL.h"

#include "FMetricsGDI.h"
#include "FLoaderGDI.h"
#include "FLoaderFreeType.h"

namespace Font
{

	Library::Library()
	{
		m_Factory = NULL;
		m_Log = NULL;

		m_LoaderNative = NULL;

		m_Library = this;
	}
	
	Library::~Library()
	{
		delete m_FontDir;

		for (std::vector<Family*>::iterator it = m_Families.begin(); it != m_Families.end(); )
		{
			delete *it;
			it = m_Families.erase(it);
		}

		for (std::vector<CharMap*>::iterator it = m_CharMaps.begin(); it != m_CharMaps.end(); )
		{
			delete *it;
			it = m_CharMaps.erase(it);
		}

		if (m_UnicodeRangeStart) { delete [] m_UnicodeRangeStart; }
		if (m_UnicodeRangeEnd) { delete [] m_UnicodeRangeEnd; }
	}

	void Library::Init()
	{
		if (!m_Factory)
		{
			m_Factory = new FactoryOpenGL(this);
		}

		if (!m_Log)
		{
			m_Log = new Log();
		}

		m_LoaderNative = new LoaderGDI(this);
		AddLoader(m_LoaderNative);

		m_LoaderFreeType = new LoaderFreeType(this);
		AddLoader(m_LoaderFreeType);

		m_UnicodeRangeTotal = 56;
		m_UnicodeRangeStart = new unsigned int[m_UnicodeRangeTotal];
		m_UnicodeRangeEnd = new unsigned int[m_UnicodeRangeTotal];

		// Regex to make life easier
		// \#define {.#} #{:z} #\/\/ {.#} \- {.*}
		// m_UnicodeRangeStart[\2] = \3; m_UnicodeRangeEnd[\2] = \4; // \1

		m_UnicodeRangeStart[ 0] = 0x0000; m_UnicodeRangeEnd[ 0] = 0x0000;
		m_UnicodeRangeStart[ 1] = 0x0000; m_UnicodeRangeEnd[ 1] = 0x007F; // FUNICODE_BASIC_LATIN
		m_UnicodeRangeStart[ 2] = 0x0080; m_UnicodeRangeEnd[ 2] = 0x00FF; // FUNICODE_LATIN_SUPPLEMENT
		m_UnicodeRangeStart[ 3] = 0x0100; m_UnicodeRangeEnd[ 3] = 0x017F; // FUNICODE_LATIN_EXTENDED_A
		m_UnicodeRangeStart[ 4] = 0x0180; m_UnicodeRangeEnd[ 4] = 0x024F; // FUNICODE_LATIN_EXTENDED_B
		m_UnicodeRangeStart[ 5] = 0x0250; m_UnicodeRangeEnd[ 5] = 0x02AF; // FUNICODE_IPA_EXTENSIONS
		m_UnicodeRangeStart[ 6] = 0x02B0; m_UnicodeRangeEnd[ 6] = 0x02FF; // FUNICODE_SPACING_MODIFIER_LETTERS
		m_UnicodeRangeStart[ 7] = 0x0300; m_UnicodeRangeEnd[ 7] = 0x036F; // FUNICODE_COMBINING_DIACRITICAL_MARKS
		m_UnicodeRangeStart[ 8] = 0x0370; m_UnicodeRangeEnd[ 8] = 0x03FF; // FUNICODE_GREEK_AND_COPTIC
		m_UnicodeRangeStart[ 9] = 0x0400; m_UnicodeRangeEnd[ 9] = 0x04FF; // FUNICODE_CYRILLIC
		m_UnicodeRangeStart[10] = 0x0500; m_UnicodeRangeEnd[10] = 0x052F; // FUNICODE_CYRILLIC_SUPPLEMENT
		m_UnicodeRangeStart[11] = 0x0530; m_UnicodeRangeEnd[11] = 0x058F; // FUNICODE_ARMENIAN
		m_UnicodeRangeStart[12] = 0x0590; m_UnicodeRangeEnd[12] = 0x05FF; // FUNICODE_HEBREW
		m_UnicodeRangeStart[13] = 0x0600; m_UnicodeRangeEnd[13] = 0x06FF; // FUNICODE_ARABIC
		m_UnicodeRangeStart[14] = 0x0700; m_UnicodeRangeEnd[14] = 0x074F; // FUNICODE_SYRIAC
		m_UnicodeRangeStart[15] = 0x0750; m_UnicodeRangeEnd[15] = 0x077F; // FUNICODE_ARABIC_SUPPLEMENT
		m_UnicodeRangeStart[16] = 0x0780; m_UnicodeRangeEnd[16] = 0x07BF; // FUNICODE_THAANA
		m_UnicodeRangeStart[17] = 0x07C0; m_UnicodeRangeEnd[17] = 0x07FF; // FUNICODE_NKO
		m_UnicodeRangeStart[18] = 0x0800; m_UnicodeRangeEnd[18] = 0x083F; // FUNICODE_SAMARITAN
		m_UnicodeRangeStart[19] = 0x0840; m_UnicodeRangeEnd[19] = 0x085F; // FUNICODE_MANDAIC
		m_UnicodeRangeStart[20] = 0x08A0; m_UnicodeRangeEnd[20] = 0x08FF; // FUNICODE_ARABIC_EXTENDED_A
		m_UnicodeRangeStart[21] = 0x0900; m_UnicodeRangeEnd[21] = 0x097F; // FUNICODE_INDIC_DEVANAGARI
		m_UnicodeRangeStart[22] = 0x0980; m_UnicodeRangeEnd[22] = 0x09FF; // FUNICODE_INDIC_BENGALI
		m_UnicodeRangeStart[23] = 0x0A00; m_UnicodeRangeEnd[23] = 0x0A7F; // FUNICODE_INDIC_GURMUKHI
		m_UnicodeRangeStart[24] = 0x0A80; m_UnicodeRangeEnd[24] = 0x0AFF; // FUNICODE_INDIC_GUJARATI
		m_UnicodeRangeStart[25] = 0x0B00; m_UnicodeRangeEnd[25] = 0x0B7F; // FUNICODE_INDIC_ORIYA
		m_UnicodeRangeStart[26] = 0x0B80; m_UnicodeRangeEnd[26] = 0x0BFF; // FUNICODE_INDIC_TAMIL
		m_UnicodeRangeStart[27] = 0x0C00; m_UnicodeRangeEnd[27] = 0x0C7F; // FUNICODE_INDIC_TELUGU
		m_UnicodeRangeStart[28] = 0x0C80; m_UnicodeRangeEnd[28] = 0x0CFF; // FUNICODE_INDIC_KANNADA
		m_UnicodeRangeStart[29] = 0x0D00; m_UnicodeRangeEnd[29] = 0x0D7F; // FUNICODE_INDIC_MALAYALAM
		m_UnicodeRangeStart[30] = 0x0D80; m_UnicodeRangeEnd[30] = 0x0DFF; // FUNICODE_INDIC_SINHALA
		m_UnicodeRangeStart[31] = 0x0E00; m_UnicodeRangeEnd[31] = 0x0E7F; // FUNICODE_THAI
		m_UnicodeRangeStart[32] = 0x0E80; m_UnicodeRangeEnd[32] = 0x0EFF; // FUNICODE_LAO
		m_UnicodeRangeStart[33] = 0x0F00; m_UnicodeRangeEnd[33] = 0x0FFF; // FUNICODE_TIBETAN
		m_UnicodeRangeStart[34] = 0x1000; m_UnicodeRangeEnd[34] = 0x109F; // FUNICODE_MYANMAR
		m_UnicodeRangeStart[35] = 0x10A0; m_UnicodeRangeEnd[35] = 0x10FF; // FUNICODE_GEORGIAN
		m_UnicodeRangeStart[36] = 0x1100; m_UnicodeRangeEnd[36] = 0x11FF; // FUNICODE_HANGUL_JAMO
		m_UnicodeRangeStart[37] = 0x1200; m_UnicodeRangeEnd[37] = 0x137F; // FUNICODE_ETHIOPIC
		m_UnicodeRangeStart[38] = 0x1380; m_UnicodeRangeEnd[38] = 0x139F; // FUNICODE_ETHIOPIC_SUPPLEMENT
		m_UnicodeRangeStart[39] = 0x13A0; m_UnicodeRangeEnd[39] = 0x13FF; // FUNICODE_CHEROKEE
		m_UnicodeRangeStart[40] = 0x1400; m_UnicodeRangeEnd[40] = 0x167F; // FUNICODE_UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS
		m_UnicodeRangeStart[41] = 0x1680; m_UnicodeRangeEnd[41] = 0x169F; // FUNICODE_OGHAM
		m_UnicodeRangeStart[42] = 0x16A0; m_UnicodeRangeEnd[42] = 0x16FF; // FUNICODE_RUNIC
		m_UnicodeRangeStart[43] = 0x1700; m_UnicodeRangeEnd[43] = 0x171F; // FUNICODE_PHILIPPINE_TAGALOG
		m_UnicodeRangeStart[44] = 0x1720; m_UnicodeRangeEnd[44] = 0x173F; // FUNICODE_PHILIPPINE_HANUNOO
		m_UnicodeRangeStart[45] = 0x1740; m_UnicodeRangeEnd[45] = 0x175F; // FUNICODE_PHILIPPINE_BUHID
		m_UnicodeRangeStart[46] = 0x1760; m_UnicodeRangeEnd[46] = 0x177F; // FUNICODE_PHILIPPINE_TAGBANWA
		m_UnicodeRangeStart[47] = 0x1780; m_UnicodeRangeEnd[47] = 0x17FF; // FUNICODE_KHMER
		m_UnicodeRangeStart[48] = 0x1800; m_UnicodeRangeEnd[48] = 0x18AF; // FUNICODE_MONGOLIAN
		m_UnicodeRangeStart[49] = 0x18B0; m_UnicodeRangeEnd[49] = 0x18FF; // FUNICODE_UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS_EXTENDED
		m_UnicodeRangeStart[50] = 0x1900; m_UnicodeRangeEnd[50] = 0x194F; // FUNICODE_LIMBU
		m_UnicodeRangeStart[51] = 0x1950; m_UnicodeRangeEnd[51] = 0x197F; // FUNICODE_TAI_LE
		m_UnicodeRangeStart[52] = 0x1980; m_UnicodeRangeEnd[52] = 0x19DF; // FUNICODE_TAI_LUE
		m_UnicodeRangeStart[53] = 0x19E0; m_UnicodeRangeEnd[53] = 0x19FF; // FUNICODE_KHMER_SYMBOLS
		m_UnicodeRangeStart[54] = 0x1A00; m_UnicodeRangeEnd[54] = 0x1A1F; // FUNICODE_BUGINESE
		m_UnicodeRangeStart[55] = 0x1A20; m_UnicodeRangeEnd[55] = 0x1AAF; // FUNICODE_TAI_THAM
	}

	void Library::LoadFontsNative()
	{
		m_LoaderNative->LoadDefault();
	}

	FaceHandle* Library::GetFontHandle(const tb::String& a_FamilyName, unsigned int a_Size, unsigned int a_Properties, CharMap* a_Map /*= NULL*/)
	{
		FaceHandle* result = NULL;

		// find the family by name

		Family* family = NULL;

		MapFamilyIter found = m_FamiliesByName.find(a_FamilyName.GetData());
		if (found != m_FamiliesByName.end())
		{
			family = found->second;

			/*// check if one of our loaders has loaded that family before

			for (size_t i = 0; i < m_Loaders.size(); i++)
			{
				result = m_Loaders[i]->FindFont(family, a_Size, a_Properties, a_Map);
				if (result)
				{
					return result;
				}
			}*/
		}
		else
		{
			family = new Family(this, a_FamilyName);
			m_Families.push_back(family);
			m_FamiliesByName.insert(std::pair<std::string, Family*>(a_FamilyName.GetData(), family));
		}

		result = family->CreateFontHandle(a_Size, a_Properties, a_Map);
		if (!result) 
		{
			// check if one of our loaders has loaded that family before

			for (size_t i = 0; i < m_Loaders.size(); i++)
			{
				result = m_Loaders[i]->FindFont(family, a_Size, a_Properties, a_Map);
				if (result) { break; }
			}
		}

		if (!result)
		{
			LOG_INFO("Trying to find family '%s' using native loader...", a_FamilyName.GetData());

			Metrics* metrics = m_LoaderNative->CreateMetrics(family, a_Properties, a_Map);
			Face* face = m_LoaderNative->CreateFace(family, metrics);
			result = face->GetHandle(a_Size, 0, a_Map);
		}

		return result;
	}

	/*bool Database::LoadFont(const tb::String& a_FileName, CharMap* a_CharMap, Style::List a_Style, Weight::List a_Weight)
	{
		CharMap* map = (a_CharMap == NULL) ? m_CharMapDefault : a_CharMap;

		FT_Face result = _LoadFace(a_FileName + ".ttf");
		if (!result)
		{
			// try default fonts
			return (_AddDefaultFace(a_FileName, map, a_Style, a_Weight) != NULL);
		}

		Style::List style = result->style_flags & FT_STYLE_FLAG_ITALIC ? Style::eItalic : Style::eNormal;
		Weight::List weight = result->style_flags & FT_STYLE_FLAG_BOLD ? Weight::eBold : Weight::eNormal;

		return (_AddFace(result, result->family_name, map, style, weight) != NULL);
	}*/

	FaceHandle* Library::GetFace(const tb::String& a_Properties)
	{
		int curr = 0;
		int property = 0;

		tb::String value("", 256);
		tb::String family_name("", 256);
		int height = 12;
		unsigned int style = FWEIGHT_NORMAL;

		int found = 0;
		do 
		{
			found = a_Properties.FindNextLeft('|', curr);
			int length = -1;
			if (found != -1)
			{
				length = found - curr;
			}
			
			value = a_Properties.GetSubMiddle(curr, length);
			if (value.GetLength() > 0)
			{
				switch (property)
				{
				case 0:
					{
						family_name = value;

						break;
					}
				case 1:
					{
						height = (int)value.ToLong();

						break;
					}
				default:
					{
						style |= _GetStyle(value);

						break;
					}
				}
			}
			
			property++;
			curr = found + 1;
		} 
		while (found != -1 && property < 5);

		return NULL;
	}

	CharMap* Library::CreateCharMap()
	{
		CharMap* result = new CharMap(this);
		return result;
	}

	CharMap* Library::GetCharMap(const tb::String& a_Name)
	{
		MapCharMapIter found = m_CharMapsByName.find(a_Name.GetData());
		if (found != m_CharMapsByName.end())
		{
			return found->second;
		}

		return NULL;
	}

	Family* Library::GetFamilyByName(const tb::String& a_Name)
	{
		MapFamilyIter found = m_FamiliesByName.find(a_Name.GetData());
		if (found != m_FamiliesByName.end())
		{
			return found->second;
		}

		LOG_ERROR("Could not find family '%s'", a_Name.GetData());
		return NULL;
	}

	unsigned int Library::_GetStyle(const tb::String& a_Name)
	{
		if (a_Name.Compare("Bold"))
		{
			return FWEIGHT_BOLD;
		}
		else if (a_Name.Compare("Italic"))
		{
			return FSTYLE_ITALIC;
		}
		else if (a_Name.Compare("Strikeout"))
		{
			return FSTYLE_STRIKEOUT;
		}
		else if (a_Name.Compare("Underline"))
		{
			return FSTYLE_UNDERLINE;
		}

		return 0;
	}

	Family* Library::CreateFamily(const tb::String& a_Name)
	{
		Family* result = NULL;

		std::map<std::string, Family*>::iterator found = m_FamiliesByName.find(a_Name.GetData());
		if (found != m_FamiliesByName.end())
		{
			result = found->second;
		}
		else
		{
			result = new Family(this, a_Name);
			m_Families.push_back(result);
			m_FamiliesByName.insert(std::pair<std::string, Family*>(a_Name.GetData(), result));
		}
		
		return result;
	}

	UnicodeRange* Library::GetUnicodeRangeByIdentifier(unsigned int a_Identifier)
	{
		if (a_Identifier < 1 || a_Identifier > m_UnicodeRangeTotal) { return NULL; }

		UnicodeRange* result = new UnicodeRange;
		result->start = m_UnicodeRangeStart[a_Identifier];
		result->end = m_UnicodeRangeEnd[a_Identifier];

		return result;
	}

	UnicodeRange* Library::GetUnicodeRangeByCharacter(unsigned int a_Character)
	{
		for (unsigned int i = 0; i < m_UnicodeRangeTotal; i++)
		{
			if (a_Character > m_UnicodeRangeEnd[i])
			{
				return NULL;
			}

			if (a_Character >= m_UnicodeRangeStart[i])
			{
				UnicodeRange* result = new UnicodeRange;
				result->start = m_UnicodeRangeStart[i];
				result->end = m_UnicodeRangeEnd[i];

				return result;
			}
		}

		return NULL;
	}

	void Library::SetFactory(Factory* a_Factory)
	{
		m_Factory = a_Factory;
	}

	Factory* Library::GetFactory()
	{
		return m_Factory;
	}

	Loader* Library::GetLoaderNative()
	{
		return m_LoaderNative;
	}

	Face* Library::LoadFontFreeType(const tb::String& a_File)
	{
		return m_LoaderFreeType->LoadFace(NULL, a_File);
	}

	void Library::AddLoader(Loader* a_Loader)
	{
		m_Loaders.push_back(a_Loader);
	}

	Log* Library::GetLog()
	{
		return m_Log;
	}

}; // namespace Font